
; AWE Argus master module

; The Argus master module is connected to the following devices:

; A VDRIVE module to PICAXE-28X1; Hardware Setup; VDRIVE PICAXE-28X1
; 1 Black GND - 0V
; 3 Red V+ - V+
; 4 Orange RXD - HTXD input6 C6
; 5 Yellow TXD - HRXD input7 C7
; 6 Green CTS - tie to 0V; Note RXD on VDRIVE2 connects to TXD on PICAXE etc.vdrive2sch.JPGsymbol first_byte = b0

; A AXE033 OLED/RTC clock module connected via I2C

; A Argus slave module connected via I2C


'******************************************************
'To do list
'- Add possible error logs to flash drive log
'	- Bootup notice, a log entry when the device has been re-booted
'	- Other possible error codes
'
'- Add euro counter
'- Clean the code and optimize variable usage
'- Check for the existence of math processor and use if present
'- Add PC-connection and develop data transmission protocol
'- Add methods for restoring calculations if the device is re-booted
'******************************************************



#picaxe 28x2
'#no_data
#no_table
#terminal 9600 ; open terminal after download


'******************Variable table************************
symbol fastmsr = b0
symbol point = b1
symbol temp = b2
symbol hour = b3
symbol minute = b4
symbol second = b5
symbol day = b6
symbol month = b7
symbol year = b8
symbol first_byte = b9


symbol str1 = b10
symbol str2 = b11
symbol str3 = b12
symbol str4 = b13
symbol str5 = b14
symbol str6 = b15

symbol oldmonth = b30

symbol chkseconds = b16
symbol chkdrvstat = b17 'Check USB drive status (1 if inserted, 0 if not)
symbol updateflag = b18
symbol tenseccounter = b34
symbol avgpointer = b35
symbol temp2 = b36
symbol avgpointer2 = b37

symbol pulsecountlow = w10
symbol pulsecounthigh = w11
symbol MeanPulseCount = w12
symbol oldpulsecount = w13
symbol PulseWidth = w14  ' This would be from Pulsin
symbol tmpWord = w19
symbol tmpWord2 = w20
symbol MeanPower = w25
symbol FastInstantPwr = w24

symbol OutsideTemp = b46

symbol EuroThisMonth = w16

symbol AddOnModulesRegister = B55		
'bit0 = math processor V3.1
'bit1 = math processor V2

symbol Scale = b54
'===============================================================================
'-------------------- uM-FPU V3.1 I2C definitions ------------------- 2011-10-17
'===============================================================================
symbol fpuID = 0xC8 ' uM-FPU I2C address
'-------------------- uM-FPU V3.1 opcode definitions ---------------------------
symbol SELECTA = 0x01 ' Select register A
symbol ATOF = 0x1E ' Convert ASCII to float, store in reg[0]
symbol FTOA = 0x1F ' Convert float to ASCII
symbol FSET = 0x20 ' reg[A] = reg[nn]
symbol FMUL = 0x24 ' reg[A] = reg[A] * reg[nn]
symbol FSET0 = 0x29 ' reg[A] = reg[0]
symbol FADDI = 0x33 ' reg[A] = reg[A] + float(bb)
symbol FDIVI = 0x37 ' reg[A] = reg[A] / float(bb)
symbol LOADWORD = 0x5B ' reg[0] = float(signed word)
symbol SYNC = 0xF0 ' Get synchronization byte
symbol READSTATUS = 0xF1 ' Read status byte
symbol READSTR = 0xF2 ' Read string from string buffer
symbol VERSION = 0xF3 ' Copy version string to string buffer
symbol SYNC_CHAR = 0x5C ' sync character
symbol LDIV = 0xA0 'reg[A] = reg[A] / reg[nn]
symbol LWRITE = 0x90 '90 nn b1,b2,b3,b4 where: nn is register number // b1, b2, b3, b4 is long integer value (b1 is MSB)
symbol LTOA = 0x9B 'Integer to ascii
symbol LREADWORD = 0x99 'Read lower 16 bit of reg A

'********************************************************





'Put all initializing code here
setup:	'*********************************************
		
		AddOnModulesRegister = 0
		
		setfreq m8
		
		'write 0,3
		'write 1,0
		'write 2,0
		'write 3,0
		'write 4,0
		
		input B.0	'Makes B.0 an input to measure pulse length
		hsersetup b9600_8,%01	' setup serial hardware at 9600 with background receive
		'low 0 ; ensure CTS is low ' CTS not connected
		avgpointer =100
		'*********************************************
		
initVdrive:
		; Send Es until the unit responds correctly
		sertxd ("<Sent> E to VDrive, waiting for response...",CR,LF)
		hserout 0,("E",CR)
		pause 1000
		gosub get_response
		if first_byte <> "E" then initVDrive
		
		
		'**********************************************
		'Check for math processor
		hi2csetup i2cmaster, fpuID, i2cslow, i2cbyte
		hi2cout 1, (0) 'reset the uM-FPU
		pause 10 ' wait for reset to complete
		hi2cout 0, (SYNC) ' check for synchronization

		hi2cin 0, (temp) ' read status byte
		'response is 255 if no Math processor is detected
		if temp = 0xC5 then 
			setbit AddOnModulesRegister,0
		endif

		sertxd("Math processor sync response:",#temp)
		
		
		'**********************************************
		
		'gosub settime
		gosub cleardisplay
		
'End of setup *********************************************


'**********************************************************
'			MAIN PROGRAM
'**********************************************************

		
		

main:
		
		if AddOnModulesRegister bit 0 set then
			gosub mp3_fastupdatewm
			gosub mp3_calceuropermonth
		endif
		
		
		'*****************************************************************************
		'i2c channel is switched to display address only once, update all display values here
		hi2csetup i2cmaster, $C6, i2cslow, i2cbyte	'switch to display i2c address
		
		gosub updatewm
		'gosub updateeuro
		gosub updatemean
		'gosub displaytemp
		gosub displaytime
		'*****************************************************************************
		
		gosub gettime
		
		'Do log entry every 10 seconds
		let chkseconds = second // $10
		if chkseconds <> 0 then let updateflag = 1:endif
		if chkseconds <> 0 or updateflag = 0 then goto main
		updateflag = 0
		
		'Start log entry
		
		'Read temperature from ds18B20 on pin B.5
		'readtemp B.5, OutsideTemp	
		
		gosub getpulses
		
		'**Disabled, using pulse length measurement instead
		'Update instant power works with 10 second update...
		'let instantpower = pulsecountlow - oldpulsecount
		'let instantpower = instantpower *36
		'oldpulsecount = pulsecountlow
		
		gosub calc2hourmean
		
		'gosub PCLog
		gosub writelog
		gosub PCDebug	
		
		goto main

end

'****************************************************
'Calculate Euro/month
'****************************************************
mp3_calceuropermonth:
		read 0,temp 'Read last month number (1=January) from eeprom
		read 1,word tmpword 'Read last months pulse count (low word)
		read 3,word tmpword2 'Read last months pusle count (high word)
		
		if month > temp then	'current month has changed
			write 0,month
			write 1,pulsecountlow
			write 3,pulsecounthigh
		endif
		
		
		
		'Switch to math processor i2c address
		hi2csetup i2cmaster, fpuID, i2cslow, i2cbyte
		
		'Subtract pulsecount from beginning of month with current pulsecount
		let tmpword = pulsecountlow - tmpword
		let tmpword2 = pulsecounthigh - tmpword2
		
		
		'Send calculation to math processor (pulsecount / 
		hi2cout 0, (LWRITE, 1,b41,b40,b39,b38,LWRITE, 2,0x00,0x01,0x45,0x85,SELECTA, 1,LDIV,2)
			
fpu_wait2:		
		hi2cin 0, (str1) ' wait for ready status
		if str1 <> 0 then fpu_wait2

		'hi2cout 0, (READSTR)	'Read result from math processor
		
		'Send command to read the lower word of reg A
		hi2cout 0, (LREADWORD)
		hi2cin 0,(b32,b33)	'Read Euro to EuroThisMonth variable
		
		return
		
		
		
		
'****************************************************
' Calculate 2 hour mean value
'****************************************************
calc2hourmean:
		inc tenseccounter
		
		if tenseccounter < 12 then: return: endif	' only execute every second minute
		
		'Switch to Slave module i2c adress and read pulses prescaled (divided by 10)
		hi2csetup i2cmaster, $A0, i2cslow, i2cbyte
		hi2cin 2, (b24,b25)	'Read into MeanPulseCount word (b24,b25)
		
		
		'Loop through scratchpad adress 100-220
		if avgpointer > 220 then: avgpointer = 100: endif
		'avgpointer2 points to last measurement in scratchpad (avgpoiter + 2 (bytes = 1 word))
		if avgpointer = 220 then
			avgpointer2 = 100
		else avgpointer2 = avgpointer + 2
		
		endif
		
		'Store mean pulse count in scratchpad
		put avgpointer, word MeanPulseCount
		
		'Get the oldest measurement in scratchpad (2 hours ago)
		get avgpointer2, word tmpWord
		
		let MeanPower = MeanPulseCount - tmpWord
		let MeanPower = MeanPower / 2		'10000imp/kWh, prescaled (divided by 10) = 1000imp/kWh -> 2 hour = 2000imp/kWh
		
		avgpointer = avgpointer + 2		'Increase pointer by 2 (2 bytes = 1 word)
		
		tenseccounter = 0
		
		return
		
		
'****************************************************
'Convert temperature from dallas ds18B20 and store in str1-3
'****************************************************
convtemp:
		if OutsideTemp > 127 then nega	'test for negative
		bintoascii OutsideTemp,str1,str2,str3
		str1 = "+"
		return
nega:
		let temp2 = OutsideTemp - 128	; adjust neg value
		bintoascii temp2,str1,str2,str3
		str1 = "-"
		return
		
'****************************************************
' Displays the time on the OLED display
'****************************************************
displaytemp:
		'Set up i2c
		'hi2csetup i2cmaster, $C6, i2cslow, i2cbyte
		pause 20
		hi2cout 0, (256,135,255)	'Set cursor to middle lower part of display
		gosub convtemp
		hi2cout 0,("T:",str1,str2,str3,255)
		return
		
		
'****************************************************
' Displays the time on the OLED display
'****************************************************
displaytime:
		'Set up i2c
		'hi2csetup i2cmaster, $C6, i2cslow, i2cbyte
		pause 10
		hi2cout 0, (254,136,255)	'Set cursor to middle lower part of display
		gosub convtime
		pause 10
		hi2cout 0,(str1,str2,":",str3,str4,":",str5,str6,255)
		return
		
'****************************************************
'Gets the time from the DS1307 RTC chip
'****************************************************		
gettime:
		hi2csetup i2cmaster, %11010000, i2cslow, i2cbyte
		hi2cin 0,(second,minute,hour,temp2,day,month,year)
		return	

'****************************************************
'Sets the time from the DS1307 RTC chip
'****************************************************		
settime:
		hi2csetup i2cmaster, %11010000, i2cslow, i2cbyte
		'sec,min,hour,dow,day,month,year
		hi2cout 0, ($25, $21, $18, $06, $03, $03, $12, $10)
		return

'****************************************************
'Gets the current pulse count from the slave module
'****************************************************		
getpulses:
		
		hi2csetup i2cmaster, $A0, i2cslow, i2cbyte
		hi2cin 0, (b20,b21)	'Read pulsecount low word from slave device
		hi2cin 4, (b22,b23)	'Read pulsecount high word from slave device
		return

'****************************************************
'Clears the display
'****************************************************		
cleardisplay:
		pause 10
		hi2csetup i2cmaster, $C6, i2cslow, i2cbyte
		pause 10
		hi2cout 0, (254,1,255)
		return

'****************************************************
'Updates the Watt meter reading
'****************************************************		
updatewm:
		
		pause 10
		hi2cout 0, (254,192,255)
		bintoascii FastInstantPwr,str1,str2,str3,str4,str5
		pause 10
		hi2cout 0,("I:",str1,str2,str3,str4,str5,"W",255)
		
		return

'****************************************************
'Updates the Euro meter reading
'****************************************************		
updateeuro:
		
		pause 10
		hi2cout 0, (254,201,255)
		bintoascii EuroThisMonth,str1,str2,str3,str4,str5
		pause 10
		hi2cout 0,("E:",str1,str2,str3,str4,str5,"E",255)
		
		return


'****************************************************
'Updates the 2 hour mean value on the display
'****************************************************		
updatemean:
		
		pause 10
		hi2cout 0, (254,128,255)
		bintoascii MeanPower,str1,str2,str3,str4,str5
		pause 10
		hi2cout 0,("A:",str1,str2,str3,str4,str5,"W",255)
		
		return
		

'****************************************************
'measures the pulse length of the input signal
'****************************************************

fastupdatewm:
		
		setfreq m4
		
		pulsin B.0,1,PulseWidth	'Measures one pulse
		
		setfreq m8
		
		let PulseWidth = PulseWidth + 338
		
		Scale = 1024 

		Do while PulseWidth > 362  ' 256 * Sqrt(2)
		PulseWidth = PulseWidth/2
		Scale = Scale /2
		Loop

		FastInstantPwr = 35157/PulseWidth * Scale  ' In Watts
		
		
		return
				
'****************************************************
'measures the pulse length of the input signal
'****************************************************

mp3_fastupdatewm:
		
		hi2csetup i2cmaster, fpuID, i2cslow, i2cbyte
		
		'****************************************
		'The frequency shifting dynamic super-duper pulse measurement is disabled
		'due to unstabile behaviour. Some kind of error handling for should be implemented.
		'Problem: When power measurement is near the point where the logic shifts between frequencys
		'faulty measurements at 16MHz occurs, this leads to power measurement of several hundreds of kilowatts.
		'****************************************
		'setfreq m16
		'pulsin B.0,1,PulseWidth	'Measures one pulse (16Mhz)
		
		'if PulseWidth = 0 then 	'If pulsemeasurement returns 0 (timeout) measure with 4MHz freq
			
		'	fastmsr=0			
			setfreq m4
			pulsin B.0,1,PulseWidth	'Measures one pulse (4MHz)
			let PulseWidth = PulseWidth + 338 'add a led light impulse length @ 4Mhz
			hi2cout 0, (LWRITE, 1,0x02,0x25,0x51,0x00,LWRITE, 2,0x00,0x00,b29,b28,SELECTA, 1,LDIV,2)
			
		'else
		'	fastmsr=1		
		'	let PulseWidth = PulseWidth + 1352 'add a led light impulse length @ 16MHz
		'	hi2cout 0, (LWRITE, 1,0x08,0x95,0x44,0x00,LWRITE, 2,0x00,0x00,b29,b28,SELECTA, 1,LDIV,2)
		'	hi2cout 0, (LTOA, 0)
		'endif
		
		'sertxd("Pulsewidth:", #PulseWidth)

		setfreq m8
		
		'if PulseWidth >65534 OR PulseWidth < 400: return: endif	'Error handling, not working yet
		
fpu_wait:		
		hi2cin 0, (str1) ' wait for ready status
		if str1 <> 0 then fpu_wait

		'hi2cout 0, (READSTR)	'Read result from math processor
		
		'Send command to read the lower word of reg A
		hi2cout 0, (LREADWORD)
		hi2cin 0,(b49,b48)	'Read Power value to FastInstantPwr
		
		return
		
		
'********************************************************
'Logs to a PC terminal program
'********************************************************
PClog:
		bintoascii pulsecounthigh,str1,str2,str3,str4,str5
		sertxd(str1,str2,str3,str4,str5)
		
		bintoascii pulsecountlow,str1,str2,str3,str4,str5
		sertxd(",",str1,str2,str3,str4,str5)
		
		gosub convtime
		sertxd(",",str1,str2,":",str3,str4,":",str5,str6)
		
		gosub convdate
		sertxd(",",str1,str2,".",str3,str4,".",str5,str6,10,13)
		
		return


'********************************************************
'Logs to a PC terminal program
'********************************************************
PCDebug:
		
		sertxd("PlsH:",#pulsecounthigh)
		sertxd("PlsL:",#pulsecountlow)
		
		gosub convtime
		sertxd(":Tim:",str1,str2,":",str3,str4,":",str5,str6)
		
		gosub convdate	'Convert BCD date to ASCII
		sertxd(":Date:",str1,str2,".",str3,str4,".",str5,str6)
		sertxd (":FastMsr:", #Fastmsr)
		sertxd (":PlsWidth:", #PulseWidth)
		sertxd (":OutsideTemp:",#OutsideTemp)
		sertxd (":Mean:",#MeanPower,10,13)
		
		
		
		return
		
'******************************************************
'Writes a log entry to the VDrive2 memory stick
'******************************************************				
writelog:
		gosub checkdrive
		if chkdrvstat = 0 then return:endif
		gosub openfile:
 		'sertxd ("<Sent> write to file")
		bintoascii pulsecounthigh,str1,str2,str3,str4,str5
		hserout 0,("wrf ",$00,$00,$00,34,CR,str1,str2,str3,str4,str5)
		
		bintoascii pulsecountlow,str1,str2,str3,str4,str5
		hserout 0,(",",str1,str2,str3,str4,str5)
		
		gosub convtemp
		hserout 0,(",",str1,str2,str3)
		
		gosub convtime
		hserout 0,(",",str1,str2,":",str3,str4,":",str5,str6)
		
		gosub convdate	'Convert BCD date to ASCII
		hserout 0,(",",str1,str2,".",str3,str4,".",str5,str6,$0A,CR)
		
		pause 30

		gosub closefile:
		return

'*****************************************************************
'Convert BCD date to ASCII. Returns (str1-6)
'*****************************************************************
convdate:		
		BcdTOASCII day,str1,str2
		BcdTOASCII month ,str3,str4
		BcdTOASCII year ,str5,str6
		return
		
'*****************************************************************
'Convert BCD time to ASCII. Returns (str1-6)
'*****************************************************************
convtime:
		BcdTOASCII hour,str1,str2
		BcdTOASCII minute ,str3,str4
		BcdTOASCII second ,str5,str6	
		return

'*****************************************************************
'Creates and/or opens a log file with the current date as filename
'*****************************************************************
openfile:
		BcdTOASCII day,str1,str2
		BcdTOASCII month ,str3,str4
		BcdTOASCII year ,str5,str6
		
		'sertxd ("<Sent> Open file",CR,LF)
		hserout 0,("opw ",str1,str2,str3,str4,str5,str6,".txt",CR)
		pause 500
		gosub get_response
		return

'*****************************************************************
'Sub procedure to receive background bytes
'*****************************************************************
get_response:
		pause 50 ; wait a while
		'sertxd ("<Response> ")
		point = 0 ; reset local pointer
		get point,first_byte ; Save the first reply byte
		do
		get point,temp ; get returned byte
		'sertxd (temp) ; transmit it
		inc point ; increment pointer
		loop while temp <> CR ; if not CR loop

		'sertxd (LF) ; Add a LF to the received CR
		'sertxd (CR,LF) ; Do another blank line
		hserptr = 0 ; reset the background receive pointer
		return
		
'*****************************************************************
; check to see if a drive is actually inserted
; response will start D for yes and N for no
'*****************************************************************
checkdrive:

		'sertxd ("<Sent> Check Drive",CR,LF)
		hserout 0,(CR)
		gosub get_response
		if first_byte <> "D" then let chkdrvstat = 0: else let chkdrvstat = 1: endif
		return

'*****************************************************************
'Closes the current file
'*****************************************************************
closefile:
		'sertxd (CR,LF)
		'sertxd ("<Sent> Close file",CR,LF)
		hserout 0,("clf ",str1,str2,str3,str4,str5,str6,".txt",CR)
		pause 1000
		gosub get_response
		if first_byte <> "D" then closefile
		return
		
		
